import json
import re

from django.utils import timezone

from apps.carts.utils import merge_cart_cookie_to_redis
from celery_tasks.email.tasks import send_verify_email
from django.contrib.auth import login, authenticate, logout
from django.http import JsonResponse
from django.views import View
from meiduo_mall.utils.secret import SecretOauth
from .utils import generate_verify_email_url
from apps.users.models import User, Address
from meiduo_mall.utils.views import LoginRequiredJSONMixin
from apps.goods.models import SKU, GoodsVisitCount
from django_redis import get_redis_connection

class UsernameCountView(View):
    def get(self, request, username):
        try:
            count = User.objects.filter(username=username).count()
        except Exception as e:
            return JsonResponse({
                "code": 400,
                "errmsg": "访问数据库失败",
            })

        return JsonResponse({
            "code": 0,
            "errmsg": 'ok',
            "count": count,
        })

#2：检验手机号是否存在
class MobileCountView(View):

    def get(self,request,mobile):
        try:
            count = User.objects.filter(mobile=mobile).count()
        except Exception as e:
            return JsonResponse({
                "code": 400,
                "errmsg": "访问数据库失败",
            })

        return JsonResponse({
            "code": 0,
            "errmsg": 'ok',
            "count": count,
        })


# 3：用户注册接口的实现

class RegisterView(View):


    def post(self, request):

        # 1:提取参数
        # body取出来的是字节
        data_json = request.body.decode()
        #将json数据转换成字典数据
        data = json.loads(data_json)

        username = data.get("username")
        password = data.get("password")
        password2 = data.get("password2")
        mobile = data.get("mobile")
        sms_code = data.get("sms_code")
        allow = data.get("allow", False)


        # 2：校验参数
        # 必要性校验
        if not all([username, password, password2, mobile, allow]):
            JsonResponse({
                "code":400,
                "errmsg": "参数缺失",
            }, status=400)

        if not allow:
            return JsonResponse({
                'code': 400,
                'errmsg': '请同意协议'
            })

        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
            return JsonResponse({
                'code': 400,
                'errmsg': '用户名格式有误！'
            })
        if not re.match(r'^[a-zA-Z0-9]{8,20}$', password):
            return JsonResponse({'code': 400, 'errmsg': 'password格式有误!'})

        if password != password2:
            return JsonResponse({'code': 400, 'errmsg': '两次输入不对!'})

        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return JsonResponse({'code': 400, 'errmsg': 'mobile格式有误!'})

        # TODO:将来实现完短信验证码接口之后，此处需要填充验证短信验证码业务
        # 验证码业务流程：
        # 连接数据库，在数据库中查询出验证码，如果没有查到，说明验证码过期了。
        # 如果验证码和用户输入不一致则验证码输入错误。
        conn = get_redis_connection("verify_code")
        sms_code_from_redis = conn.get("sms_%s" % mobile)
        if not sms_code_from_redis:
            return JsonResponse({
                "code":400,
                "errmsg": "验证码过期",
            }, status=403)

        if sms_code != sms_code_from_redis.decode() :
            return JsonResponse({
                "code": 400,
                "errmsg": "验证码错误",
            }, status=400)

        # 3：业务处理---创建用户+状态保持
        try:
            user = User.objects.create_user(
                username=username,
                password=password,
                mobile=mobile,
            )
        except Exception as e:
            return JsonResponse({
                "code": 400,
                "errmsg": "用户已经存在"
            },status=500)
        # TODO:当用户注册成功，相当于用户登陆，我们需要记录用户信息 —— 状态保持
        # (1)、把用户数据写入session缓存——redis
        # (2)、在返回当时候，把sessionid设置在浏览器当Cookie中
        login(request, user)
        # 4：构建响应
        response = JsonResponse({'code': 0, 'errmsg': 'ok'})

        response.set_cookie(key="username", value=user.username, max_age=3600 * 24 * 14)

        return response
        # return merge_cart_cookie_to_redis(request, response)


class LoginView(View):

    def post(self, request):
        '''实现登录接口'''
        # 1.接收参数
        data_dict = json.loads(request.body.decode())
        username = data_dict.get('username')
        password = data_dict.get('password')
        remembered = data_dict.get('remembered')


        # 2.校验(整体 + 单个)
        if not all([username, password]):
            return JsonResponse({'code': 400,'errmsg': '缺少必传参数'})

        # 3.验证是否能够登录
        user = authenticate(request, username=username, password=password)

        # 判断是否为空,如果为空,返回
        if user is None:
            return JsonResponse({'code': 400, 'errmsg': '用户名或者密码错误'})

        # 5.判断是否记住用户
        if remembered != True:
            # 7.如果没有记住: session立刻失效
            request.session.set_expiry(0)
        else:
            # 6.如果记住:  session设置为两周有效
            request.session.set_expiry(None)
        login(request, user)
        # 8.返回json
        response = JsonResponse({'code': 0, 'errmsg': 'ok'})

        response.set_cookie(key="username", value=user.username, max_age=3600*24*14)


        # return response
        return merge_cart_cookie_to_redis(request,response)



 # 定义退出登陆方法
class LogoutView(View):
    def delete(self, request):

        # 清理session，这里清除的是redis数据库中的值
        logout(request)

        response = JsonResponse({
            "code": 200,
            "errmsg": "ok"
        })

        # 清除前端页面的cookie值
        response.delete_cookie("username")

        return response

from django.contrib.auth.mixins import LoginRequiredMixin
# 用户中心页面构建
# 实际上django已经封装了判断用户是否登陆的类
# 注意：如果一个视图继承了LoginRequiredMixin，那么这个视图中的所有的接口必须登陆才能访问。

# 注意LoginRequiredMixin默认如果未登录返回是django自己的页面，我们不需要，所以需要重写LoginRequiredMixin



class UseInfoView(LoginRequiredJSONMixin,View):
    # 1:验证用户是否登陆
    # request.user:如果用户已经登陆了（本质是传到后端的cookie中携带有sessionid）
    # 那么请求的时候，后端会根据sessionid查找用户数据，如果查找成功，则把request.user
    # 作为查找到的用户模型类对象：一句话：request.user就是当前登陆的用户模型类对象。


    #注意：如果没有登录，发送请求，那么request.user 是匿名对象anonymous。
    # 注意：request.user有两个方法：
    # request.user.is_authenticated，这个是说已经认证的用户
    # request.user.is_anonymous，这个是说是个匿名用户
    # 2：获取用户对象，构造响应对象

    def get(self,request):
        # if not request.user.is_authenticated:
        #     return JsonResponse({
        #         "code": 0,
        #         "errmsg": "用户未登录",
        #     })

        return JsonResponse({
            "code":0,
            "errmsg": "ok",
            "info_data": {
                'username': request.user.username,
                'mobile': request.user.mobile,
                'email': request.user.email,
                'email_active': request.user.email_active,
            }
        })


# 邮箱验证
class EmailView(LoginRequiredJSONMixin,View):

    def put(self,request):
        # 1:提取参数
        data = json.loads(request.body.decode())
        email = data.get("email")
        # 2:校验参数
        if not data:
            return JsonResponse({
                "code": 400,
                "errmsg": "参数缺失",
            })

        if not re.match("^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$", email):
            return JsonResponse({
                "code": 400,
                "errmsg": "邮箱格式错误"
            })

        # 3：数据处理---获取邮箱信息，写入数据库  此外还要发送邮箱

        # 获取当前的用户对象
        user = request.user
        try:
            user.email = email
            user.save()
        except Exception as e:
            JsonResponse({
                "code": 400,
                "errmsg": "邮箱获取失败",
            }, status=400)


        # 发送邮箱

        # 1：导入celery_task模块，
        # 2：调用发送的函数:
        verify_url = generate_verify_email_url(request)
        send_verify_email.delay(email, verify_url)


        # 4:返回响应信息

        return JsonResponse({
            "code":0,
            "errmsg" : "ok",
        })

# 逻辑：接受tocken值，然后对tocken值进行解密，与当前用户在数据库中的id和email进行匹配，
# 如果匹配成功，将user.email_active设置成true
class VerifyEmailView(View):
    def put(self, request):
        # 1:提取参数
        token = request.GET.get("token")

        # 2:校验参数
        # 存在性校验
        if not token:
            JsonResponse({
                "code": 400,
                "errmsg": "参数缺失",
            })

        # 检验tocken是否是原来发送出去的
        # 对tocken进行解密,解密后是个字典数据
        data_dict = SecretOauth().loads(token)

        # 获取字典中的user_id和mobile
        user_id = data_dict.get("user_id")
        email = data_dict.get("email")

        # 根据这两个字段获取数据库中的user对象
        try:
            user = User.objects.get(pk=user_id, email=email)
        except Exception as e:
            JsonResponse({
                "code": 400,
                "errmsg": "数据库查询不到用户，怀疑用户伪造"
            })

        # 3：数据处理---将user.email_active设置成true
        try:
            user.email_active = True
            user.save()
        except Exception as e:
            JsonResponse({
                "code": 400,
                "errmsg": "数据库错误，激活失败"
            })

        # 4:返回响应
        return JsonResponse({
            "code": 200,
            "errmsg": "ok"
        })

# 新增收货地址

# 思路：将前端传来的数据保存在数据库再返回给前段，实现局部刷新。
class  CreateAddressView(View):

    def post(self, request):

        # 1:接受参数
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 2：校验参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return JsonResponse({'code': 400,
                                      'errmsg': '缺少必传参数'})

        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return JsonResponse({'code': 400, 'errmsg': '参数mobile有误'})

        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return JsonResponse({'code': 400, 'errmsg': '参数tel有误'})
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return JsonResponse({'code': 400, 'errmsg': '参数email有误'})


        #3： 数据处理：将接受到的参数保存到数据库
        try:
            # address = Address.objects.create(**json_dict)

            address = Address.objects.create(
                user=request.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
            # 检测有没有默认地址，如果没有默认地址，将这个设值成默认地址
            if not request.user.default_address:
                request.user.default_address = address
                request.user.save()

        except Exception as e:
            JsonResponse({
                "code": 400,
                "errmsg": "收货地址信息保存到数据库失败"
            })

        # 新增地址成功，现在将新增的地址进行刷新，还给前端显示。
        address_dict = dict(**json_dict)

        return JsonResponse({
            "code": 0,
            "errmsg": "ok",
            "address": address_dict
        })



# 展示地址
# 思路：获取所有的列表，然后遍历列表，对列表进行拆包，最后返回列表和默认地址id
class AddressView(View):

    def get(self, request):
        # 必须逻辑删除的地址为False
        # addresses是个查询集对象<QuerySet [<Addresses: Addresses object (1)>]>

        # 方案一：
        # addresses = Address.objects.filter(user=request.user, is_deleted=False)
        # # 使用model_to_dict直接将查询集对象转化成字典格式
        # # 使用列表推导式构造列表
        # address_dict_list = [model_to_dict(address) for address in addresses ]

        # 方案二：
        addresses = Address.objects.filter(user=request.user, is_deleted=False).values()
        # <QuerySet [{'district_id': 110101, 'province_id': 110000, 'title': '任善文', 'tel': ''>
        address_dict_list = list(addresses)
        print(address_dict_list)
        default_id = request.user.default_address_id

        return JsonResponse({
            "code": 0,
            "errmsg": "ok",
            "addresses":address_dict_list,
            "default_address_id" : default_id,
        })


# 修改地址和删除地址
class UpdateDestroyAddressView(View):
    """修改和删除地址"""

    def put(self, request, address_id):
        """修改地址"""
        # 接收参数
        address_id = int(address_id)
        json_dict = json.loads(request.body.decode())

        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 校验参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return JsonResponse({'code': 400,
                                      'errmsg': '缺少必传参数'})


        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return JsonResponse({'code': 400,
                                      'errmsg': '参数mobile有误'})

        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return JsonResponse({'code': 400,
                                          'errmsg': '参数tel有误'})
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return JsonResponse({'code': 400,
                                          'errmsg': '参数email有误'})

        # 判断地址是否存在,并更新地址信息
        try:
            Address.objects.filter(id=address_id).update(
                user = request.user,
                title = receiver,
                receiver = receiver,
                province_id = province_id,
                city_id = city_id,
                district_id = district_id,
                place = place,
                mobile = mobile,
                tel = tel,
                email = email
            )
        except Exception as e:
            return JsonResponse({'code': 400,
                                      'errmsg': '更新地址失败'})

        # 响应更新地址结果
        return JsonResponse({'code': 0,
                                  'errmsg': '更新地址成功',
                                  'address': json_dict})


    def delete(self, request, address_id):

        try:
            address = Address.objects.get(id = address_id)

            address.is_deleted = True
            address.save()

        except Exception as e:
            JsonResponse({
                "code": 400,
                "errmsg": "删除地址失败"
            })

        return JsonResponse({
            "code": 0,
            "errmsg": "ok"
        })

# 设置默认地址
class DefaultAddressView(View):

    def put(self, request, address_id):

        try:
            address = Address.objects.get(id=address_id)
            request.user.default_address = address
            request.user.save()

        except Exception as e:
            JsonResponse({
                "code": 400,
                "errmsg": "设置默认地址失败"
            })

        return JsonResponse({
            "code": 0,
            "errmsg": "ok"
        })

# 修改默认地址标题：

class  UpdateTitleAddressView(View):

    def put(self, request, address_id):
        # 1:获取新默认标题
        request_data = request.body.decode()
        data_dict = json.loads(request_data)
        title = data_dict.get("title")
        try:
            address = Address.objects.get(id= address_id)

            address.title = title
            address.save()

        except Exception as e:
            JsonResponse({
                "code": 400,
                "errmsg": "设置默认标题失败"
            })

        return JsonResponse({
            "code": 0,
            "errmsg": "ok"
        })

# 修改密码
class ChangePasswordView(LoginRequiredJSONMixin, View):

    def put(self, request):
        # 1:提取参数:request.body是个二进制数据，所以需要转码成字符串类型(json)
        request_json = request.body.decode()
        # 将json格式转换成字典类型
        request_dict = json.loads(request_json)

        old_password = request_dict.get("old_password")
        new_password = request_dict.get("new_password")
        new_password2 = request_dict.get("new_password2")
        # 2:校验参数

        # 校验参数
        if not all([old_password, new_password, new_password2]):
            return JsonResponse({'code': 400,
                                      'errmsg': '缺少必传参数'})

        result = request.user.check_password(old_password)
        if not result:
            return JsonResponse({'code': 400,
                                      'errmsg': '原始密码不正确'})

        if not re.match(r'^[0-9A-Za-z]{8,20}$', new_password):
            return JsonResponse({'code': 400,
                                      'errmsg': '密码最少8位,最长20位'})

        if new_password != new_password2:
            return JsonResponse({'code': 400,
                                      'errmsg': '两次输入密码不一致'})

        # 3：数据处理---设置新密码---退出状态保持---干掉前端cookie
        try:
            request.user.set_password(new_password)
            request.user.save()
        except Exception as e:
            JsonResponse({
                "code": 400,
                "errmsg": "新密码设置失败"
            })

        logout(request)

        response = JsonResponse({
            "code": 0,
            "errmsg": "ok"
        })

        response.delete_cookie("username")

        return response


# 用户浏览历史记录
class UserBrowseHistory(LoginRequiredJSONMixin, View):

    def post(self, request):

        user = request.user
        # 1、提取参数
        # 2、校验参数
        data = json.loads(request.body.decode())
        sku_id = data.get('sku_id')
        if not sku_id:
            return JsonResponse({
                'code': 400,
                'errmsg': '缺少参数'
            }, status=400)

        try:
            sku = SKU.objects.get(pk=sku_id, is_launched=True)
        except SKU.DoesNotExist as e:
            return JsonResponse({
                'code': 404,
                'errmsg': '商品已下架/不存在'
            }, status=404)

        # 3、数据/业务处理 —— 把访问的sku的id写入redis表示记录一条浏览历史

        # 3.1、获取"history"缓存配置的redis链接
        conn = get_redis_connection('history')
        p = conn.pipeline()
        # 3.2、历史记录写入缓存
        # 3.2.1、去重
        p.lrem(
            'history_%d' % user.id,
            0,  # 删除所有指定成员
            sku_id
        )
        # 3.2.2、插入列表头
        p.lpush(
            'history_%d' % user.id,
            sku_id
        )
        # 3.2.3、截断保留5个记录
        p.ltrim(
            'history_%d' % user.id,
            0,
            4
        )
        p.execute()  # 批量执行redis指令


        # TODO：商品访问量的记录
        cur_0_time = timezone.localtime().replace(hour=0, minute=0, second=0)

        try:
            visit_obj = GoodsVisitCount.objects.get(
                category_id=sku.category_id,
                create_time__gte=cur_0_time
            )
        except GoodsVisitCount.DoesNotExist as e:
            GoodsVisitCount.objects.create(
                category_id=sku.category_id,
                count=1
            )
        else:
            visit_obj.count += 1
            visit_obj.save()


        # 4、构建响应
        return JsonResponse({
            'code': 0,
            'errmsg': 'ok'
        })



    # 查看历史
    def get(self, request):
        user = request.user
        # 1、提取参数
        # 2、校验参数
        # 3、数据/业务处理 —— 用户浏览的sku商品信息返回(读redis获取最近浏览的历史sku.id, 读mysql获取sku详细信息)
        # 3.1、读redis获取浏览历史
        conn = get_redis_connection('history')
        # sku_ids = [b'6', b'3', b'4', b'14', b'15']
        sku_ids = conn.lrange(
            'history_%d' % user.id,
            0,
            -1
        )

        skus = []  # 用于记录返回sku商品的详细信息
        # 3.2、读mysql获取详细信息
        for sku_id in sku_ids:
            # sku_id = b'6'
            sku = SKU.objects.get(pk=int(sku_id))
            skus.append({
                'id': sku.id,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price
            })


        # 4、构建响应
        return JsonResponse({
            'code': 0,
            'errmsg': 'ok',
            'skus': skus
        })



















